// 解说文本实体类：用于显示游戏中的文字说明，支持打字机效果和快进功能
class Exposition extends Entity {
    // 构造函数：初始化解说文本
    // @param lines: 文本行数组
    // @param skippable: 是否可以快进
    constructor(lines, skippable) {
        super();
        this.buckets.push('exposition');
        this.lines = lines;
        this.text = this.lines.join('\n');
        // 计算总字符数
        this.totalChars = this.lines.reduce((acc, line) => acc + line.length, 0);

        this.skippable = skippable;

        // 用于缓存计算的行宽度
        this.computedLinesCanvasWidth = 0;
    }

    // 获取当前显示的最后一个字符
    get latestChar() {
        return this.text.charAt(this.visibleChars - 1);
    }

    // 计算当前应显示的字符数（基于时间的打字机效果）
        /**
     * 获取当前可见的字符数。
     * @returns {number} 当前可见的字符数，最大不超过总字符数。
     */
    get visibleChars() {
        return min(this.totalChars, ~~(this.age * 15));
    }

    // 等待文本完全显示
    // @returns: Promise，当所有文本显示完成时解决
        /**
     * 检查是否完成显示。
     * @returns {Promise} 当可见字符数达到总字符数时解析的Promise。
     */
    complete() {
        return this.world.waitFor(() => this.visibleChars >= this.totalChars);
    }

    // 更新逻辑
    // @param elapsed: 经过的时间
    cycle(elapsed) {
        const { visibleChars } = this;

        // 处理快进逻辑（空格键或触摸）
        if (this.skippable && (DOWN[32] || TOUCHES.length)) elapsed *= 8;

        super.cycle(elapsed);

        // 播放字符出现音效（非空格和换行字符）
        if (visibleChars < this.visibleChars && this.latestChar !== ' ' && this.latestChar !== '\n') {
            if (this.previousSound) {
                this.previousSound.stop();
            }
            this.previousSound = zzfx(...[.2,,634,,.01,.02,3,3.3,,,,,,.1,278,,,.95,.03,,518]); // Blip 687
            this.previousSound.start();
        }
    }

    // 计算文本换行
    // @param availableWidth: 可用宽度
    // @returns: 计算后的文本行数组
    calculateLines(availableWidth) {
        const newLines = [];

        ctx.font = nomangle('18pt Courier');

        for (const line of this.lines) {
            let currentLine = '';
            let currentLineWidth = 0;

            let i = 0;
            // 逐词分析并处理换行
            while (i < line.length) {
                let k = i;

                // 找到下一个单词
                while (k < line.length && line.charAt(k) !== ' ') {
                    k++;
                }

                const addedWord = line.slice(i, k + 1);
                const addedWordWidth = ctx.measureText(addedWord).width;
                if (k > i) {
                    if (addedWordWidth + currentLineWidth < availableWidth) {
                        // 单词能放入当前行
                        currentLine += addedWord;
                        currentLineWidth += addedWordWidth;
                    } else {
                        // 单词放不下，开始新行
                        newLines.push(currentLine);
                        currentLine = addedWord;
                        currentLineWidth = addedWordWidth;
                    }
                } else {
                    currentLine += addedWord;
                    currentLineWidth += addedWordWidth;
                }

                i = k + 1;
            }

            // 处理最后一行
            if (currentLine.length) {
                newLines.push(currentLine);
            }
        }

        return newLines;
    }

    // 渲染文本
    // @param camera: 当前相机实体
    render(camera) {
        // 设置渲染位置（相对于相机）
        ctx.translate(~~camera.x - CANVAS_WIDTH / 2, ~~camera.y - CANVAS_HEIGHT / 2);

        // 设置文本样式
        ctx.fillStyle = '#fff';
        ctx.shadowColor = '#000';
        ctx.shadowOffsetY = 2;
        ctx.font = nomangle('18pt Courier');
        ctx.textAlign = nomangle('left');
        ctx.textBaseline = nomangle('middle');

        // 如果画布宽度改变，重新计算行布局
        if (this.computedLinesCanvasWidth !== CANVAS_WIDTH) {
            this.computedLinesCanvasWidth = CANVAS_WIDTH;
            this.computedLines = this.calculateLines(CANVAS_WIDTH - 100);
        }

        // 计算起始Y坐标（垂直居中）
        let y = CANVAS_HEIGHT / 2 - (this.computedLines.length * 25) / 2;

        // 找出最长的行
        let longestLine = 0;
        for (const line of this.computedLines) {
            longestLine = max(ctx.measureText(line).width, longestLine);
        }

        // 逐行渲染文本
        let charCount = 0;
        for (const line of this.computedLines) {
            ctx.fillText(line.slice(0, max(0, this.visibleChars - charCount)), (CANVAS_WIDTH - longestLine) / 2, y);
            y += 25;
            charCount += line.length;
        }

        // 显示快进提示（如果可快进）
        if (this.skippable) {
            ctx.textAlign = nomangle('right');
            ctx.fillText(
                inputMode === INPUT_MODE_KEYBOARD
                    ? nomangle('按住 [空格键] 快进')
                    : nomangle('按住快进'),
                CANVAS_WIDTH - 50,
                CANVAS_HEIGHT - 50,
            );
        }
    }
}
